#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_slave.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define TAG "PS2_EMU"

// PS2引脚定义
#define PS2_SPI_HOST    HSPI_HOST
#define PS2_DATA_GPIO   12  // MISO
#define PS2_CMD_GPIO    13  // MOSI
#define PS2_CLK_GPIO    15  // SCK
#define PS2_CS_GPIO     14  // SS
#define PS2_ACK_GPIO    2   // ACK (可选)

// PS2命令定义
#define PS2_CMD_READ_DATA   0x42
#define PS2_CMD_ENTER_CONFIG 0x43
#define PS2_CMD_EXIT_CONFIG  0x44
#define PS2_CMD_SET_MODE     0x45
#define PS2_CMD_READ_MODEL   0x46
#define PS2_CMD_READ_VERSION 0x47

// 手柄状态结构体
typedef struct {
    uint8_t buttons_l;  // 低字节按钮状态
    uint8_t buttons_h;  // 高字节按钮状态
    uint8_t right_x;    // 右摇杆X
    uint8_t right_y;    // 右摇杆Y
    uint8_t left_x;     // 左摇杆X
    uint8_t left_y;     // 左摇杆Y
} ps2_gamepad_state_t;

// 全局手柄状态
static ps2_gamepad_state_t gamepad_state = {
    .buttons_l = 0xFF,  // 默认所有按钮释放
    .buttons_h = 0xFF,
    .right_x = 0x80,    // 摇杆中间位置
    .right_y = 0x80,
    .left_x = 0x80,
    .left_y = 0x80
};

// SPI从机初始化
void spi_slave_init(void)
{
    // 配置SPI总线
    spi_bus_config_t buscfg = {
        .miso_io_num = PS2_DATA_GPIO,
        .mosi_io_num = PS2_CMD_GPIO,
        .sclk_io_num = PS2_CLK_GPIO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };

    // 配置SPI从机接口
    spi_slave_interface_config_t slvcfg = {
        .spics_io_num = PS2_CS_GPIO,
        .flags = 0,
        .queue_size = 1,
        .mode = 3,  // PS2使用SPI模式3
        .post_setup_cb = NULL,
        .post_trans_cb = NULL
    };

    // 初始化SPI从机
    esp_err_t ret = spi_slave_initialize(PS2_SPI_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SPI slave init failed");
        return;
    }
    
    // 初始化ACK引脚（可选）
    gpio_config_t ack_conf = {
        .pin_bit_mask = (1ULL << PS2_ACK_GPIO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&ack_conf);
    gpio_set_level(PS2_ACK_GPIO, 1);
}

// 处理PS2命令
uint8_t handle_ps2_command(uint8_t cmd)
{
    static uint8_t config_mode = 0;
    static uint8_t cmd_buffer[32];
    static uint8_t cmd_index = 0;
    
    switch(cmd) {
        case PS2_CMD_READ_DATA:
            // 返回手柄数据
            return ((uint8_t*)&gamepad_state)[cmd_index++ % sizeof(gamepad_state)];
            
        case PS2_CMD_ENTER_CONFIG:
            config_mode = 1;
            return 0xF3; // 确认
            
        case PS2_CMD_EXIT_CONFIG:
            config_mode = 0;
            return 0xF3; // 确认
            
        case PS2_CMD_SET_MODE:
            // 设置模式（模拟/数字）
            return 0xF3; // 确认
            
        case PS2_CMD_READ_MODEL:
            // 返回型号信息
            return 0x03; // 模拟手柄
            
        case PS2_CMD_READ_VERSION:
            // 返回版本信息
            return 0x01; // 版本1.0
            
        default:
            if (config_mode) {
                // 配置模式下的其他命令
                return 0xF3; // 确认
            }
            return 0xFF; // 无效命令
    }
}

// SPI通信任务
void spi_task(void *arg)
{
    spi_slave_transaction_t t;
    memset(&t, 0, sizeof(t));
    
    while(1) {
        // 准备接收数据
        t.length = 8; // 每次传输8位
        t.rx_buffer = NULL;
        t.tx_buffer = NULL;
        
        // 等待主机选择从机
        esp_err_t ret = spi_slave_transmit(PS2_SPI_HOST, &t, portMAX_DELAY);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "SPI transmit error");
            continue;
        }
        
        // 处理接收到的命令
        uint8_t cmd = *(uint8_t*)t.rx_data;
        uint8_t response = handle_ps2_command(cmd);
        
        // 发送响应
        t.tx_data[0] = response;
        t.length = 8;
        
        // 如果需要，发送ACK信号
        gpio_set_level(PS2_ACK_GPIO, 0);
        vTaskDelay(1 / portTICK_PERIOD_MS);
        gpio_set_level(PS2_ACK_GPIO, 1);
    }
}

// 设置按钮状态
void set_button_state(uint8_t button, uint8_t state)
{
    // 这里根据实际按钮映射设置gamepad_state
    // 示例：设置选择按钮
    if (button == 0) { // 假设button 0是选择按钮
        if (state) {
            gamepad_state.buttons_l &= ~(1 << 0);
        } else {
            gamepad_state.buttons_l |= (1 << 0);
        }
    }
    // 其他按钮类似处理...
}

// 设置摇杆值
void set_joystick(uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry)
{
    gamepad_state.left_x = lx;
    gamepad_state.left_y = ly;
    gamepad_state.right_x = rx;
    gamepad_state.right_y = ry;
}

void app_main(void)
{
    // 初始化SPI从机
    spi_slave_init();
    
    // 创建SPI通信任务
    xTaskCreate(spi_task, "spi_task", 4096, NULL, 10, NULL);
    
    // 初始化手柄状态
    memset(&gamepad_state, 0xFF, sizeof(gamepad_state));
    gamepad_state.left_x = 0x80;
    gamepad_state.left_y = 0x80;
    gamepad_state.right_x = 0x80;
    gamepad_state.right_y = 0x80;
    
    ESP_LOGI(TAG, "PS2手柄模拟器已启动");
    
    // 主循环中可以更新手柄状态
    while(1) {
        // 这里可以添加代码来更新手柄状态
        // 例如从其他输入设备读取数据并更新gamepad_state
        
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
}



#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_slave.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_err.h"

#define TAG "PS2_EMULATOR"

// 引脚定义
#define PS2_SPI_HOST        HSPI_HOST
#define PS2_DATA_GPIO       12  // MISO
#define PS2_CMD_GPIO        13  // MOSI
#define PS2_CLK_GPIO        15  // SCK
#define PS2_CS_GPIO         14  // SS
#define PS2_ACK_GPIO        2   // ACK

// PS2协议命令
#define PS2_CMD_READ_DATA       0x42
#define PS2_CMD_ENTER_CONFIG    0x43
#define PS2_CMD_EXIT_CONFIG     0x44
#define PS2_CMD_SET_MODE        0x45
#define PS2_CMD_READ_MODEL      0x46
#define PS2_CMD_READ_VERSION    0x47
#define PS2_CMD_ENABLE_VIB      0x4D

// 按钮位掩码
#define PS2_BTN_SELECT       (1 << 0)
#define PS2_BTN_L3           (1 << 1)
#define PS2_BTN_R3           (1 << 2)
#define PS2_BTN_START        (1 << 3)
#define PS2_BTN_UP           (1 << 4)
#define PS2_BTN_RIGHT        (1 << 5)
#define PS2_BTN_DOWN         (1 << 6)
#define PS2_BTN_LEFT         (1 << 7)
#define PS2_BTN_L2           (1 << 8)
#define PS2_BTN_R2           (1 << 9)
#define PS2_BTN_L1           (1 << 10)
#define PS2_BTN_R1           (1 << 11)
#define PS2_BTN_TRIANGLE     (1 << 12)
#define PS2_BTN_CIRCLE       (1 << 13)
#define PS2_BTN_CROSS        (1 << 14)
#define PS2_BTN_SQUARE       (1 << 15)

// 手柄状态结构
typedef struct {
    uint16_t buttons;       // 16位按钮状态
    uint8_t right_x;        // 右摇杆X (0-255, 128=中心)
    uint8_t right_y;        // 右摇杆Y
    uint8_t left_x;         // 左摇杆X
    uint8_t left_y;         // 左摇杆Y
    uint8_t vibration_r;    // 右马达振动强度
    uint8_t vibration_l;    // 左马达振动强度
    uint8_t config_mode;    // 配置模式标志
} ps2_gamepad_state_t;

// 全局手柄状态
static ps2_gamepad_state_t gamepad_state = {
    .buttons = 0xFFFF,      // 所有按钮初始为释放状态
    .right_x = 0x80,
    .right_y = 0x80,
    .left_x = 0x80,
    .left_y = 0x80,
    .vibration_r = 0,
    .vibration_l = 0,
    .config_mode = 0
};

// 初始化SPI从机
void init_spi_slave() {
    // 配置SPI总线
    spi_bus_config_t buscfg = {
        .miso_io_num = PS2_DATA_GPIO,
        .mosi_io_num = PS2_CMD_GPIO,
        .sclk_io_num = PS2_CLK_GPIO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 32
    };

    // 配置SPI从机
    spi_slave_interface_config_t slvcfg = {
        .spics_io_num = PS2_CS_GPIO,
        .flags = 0,
        .queue_size = 3,
        .mode = 3,          // PS2使用SPI模式3(CPOL=1, CPHA=1)
        .post_setup_cb = NULL,
        .post_trans_cb = NULL
    };

    // 初始化SPI从机
    ESP_ERROR_CHECK(spi_slave_initialize(PS2_SPI_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
    
    // 配置ACK引脚
    gpio_config_t ack_conf = {
        .pin_bit_mask = (1ULL << PS2_ACK_GPIO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&ack_conf);
    gpio_set_level(PS2_ACK_GPIO, 1);
}

// 处理PS2命令
uint8_t handle_ps2_command(uint8_t cmd, uint8_t *tx_buf) {
    static uint8_t cmd_index = 0;
    uint8_t response = 0xFF; // 默认响应
    
    switch(cmd) {
        case PS2_CMD_READ_DATA:
            // 构建响应数据包
            tx_buf[0] = 0xFF;               // 设备ID
            tx_buf[1] = gamepad_state.buttons & 0xFF;        // 低字节按钮
            tx_buf[2] = (gamepad_state.buttons >> 8) & 0xFF; // 高字节按钮
            tx_buf[3] = gamepad_state.right_x;               // 右摇杆X
            tx_buf[4] = gamepad_state.right_y;               // 右摇杆Y
            tx_buf[5] = gamepad_state.left_x;                // 左摇杆X
            tx_buf[6] = gamepad_state.left_y;                // 左摇杆Y
            response = tx_buf[cmd_index++ % 7];
            break;
            
        case PS2_CMD_ENTER_CONFIG:
            gamepad_state.config_mode = 1;
            response = 0xF3; // 确认响应
            break;
            
        case PS2_CMD_EXIT_CONFIG:
            gamepad_state.config_mode = 0;
            response = 0xF3; // 确认响应
            break;
            
        case PS2_CMD_SET_MODE:
            // 设置模式(0x00=数字, 0x01=模拟)
            response = 0xF3; // 确认响应
            break;
            
        case PS2_CMD_READ_MODEL:
            response = 0x03; // 模拟手柄
            break;
            
        case PS2_CMD_READ_VERSION:
            response = 0x01; // 版本1.0
            break;
            
        case PS2_CMD_ENABLE_VIB:
            // 设置振动马达
            if(gamepad_state.config_mode) {
                gamepad_state.vibration_r = tx_buf[1];
                gamepad_state.vibration_l = tx_buf[2];
                response = 0xF3; // 确认响应
            }
            break;
            
        default:
            if(gamepad_state.config_mode) {
                response = 0xF3; // 配置模式下的确认响应
            }
            break;
    }
    
    return response;
}

// SPI通信任务
void spi_communication_task(void *arg) {
    spi_slave_transaction_t t;
    uint8_t rx_buf[32] = {0};
    uint8_t tx_buf[32] = {0};
    
    memset(&t, 0, sizeof(t));
    t.length = 8 * sizeof(rx_buf);
    t.rx_buffer = rx_buf;
    t.tx_buffer = tx_buf;
    
    while(1) {
        // 等待主机通信
        ESP_ERROR_CHECK(spi_slave_transmit(PS2_SPI_HOST, &t, portMAX_DELAY));
        
        // 处理接收到的命令
        uint8_t cmd = rx_buf[0];
        uint8_t response = handle_ps2_command(cmd, tx_buf);
        
        // 准备响应
        tx_buf[0] = response;
        t.length = 8; // 单字节响应
        
        // 发送ACK信号
        gpio_set_level(PS2_ACK_GPIO, 0);
        ets_delay_us(10); // 约10us的ACK脉冲
        gpio_set_level(PS2_ACK_GPIO, 1);
        
        // 重置事务长度
        t.length = 8 * sizeof(rx_buf);
    }
}

// 更新按钮状态
void update_button_state(uint16_t button_mask, uint8_t pressed) {
    if(pressed) {
        gamepad_state.buttons &= ~button_mask;
    } else {
        gamepad_state.buttons |= button_mask;
    }
}

// 更新摇杆值
void update_joystick(uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry) {
    gamepad_state.left_x = lx;
    gamepad_state.left_y = ly;
    gamepad_state.right_x = rx;
    gamepad_state.right_y = ry;
}

void app_main() {
    // 初始化SPI从机
    init_spi_slave();
    
    // 创建SPI通信任务
    xTaskCreate(spi_communication_task, "ps2_spi_task", 4096, NULL, 10, NULL);
    
    ESP_LOGI(TAG, "PS2手柄模拟器已启动");
    
    // 主循环中可以更新手柄状态
    while(1) {
        // 示例：每5秒切换一次选择按钮状态
        static uint8_t select_state = 0;
        select_state = !select_state;
        update_button_state(PS2_BTN_SELECT, select_state);
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}





/*  
 * ESP32模拟PS2手柄协议实现  
 * 基于ESP-IDF框架  
 */  
  
#include <stdio.h>  
#include "freertos/FreeRTOS.h"  
#include "freertos/task.h"  
#include "driver/gpio.h"  
#include "esp_log.h"  
  
// 定义PS2手柄引脚  
#define PS2_DAT_GPIO 19  // MISO  
#define PS2_CMD_GPIO 23  // MOSI  
#define PS2_CLK_GPIO 18  // CLK  
#define PS2_CS_GPIO  5   // CS  
  
// 定义日志标签  
static const char* TAG = "PS2_CONTROLLER";  
  
// 初始化GPIO  
void ps2_gpio_init() {  
    gpio_reset_pin(PS2_DAT_GPIO);  
    gpio_reset_pin(PS2_CMD_GPIO);  
    gpio_reset_pin(PS2_CLK_GPIO);  
    gpio_reset_pin(PS2_CS_GPIO);  
  
    gpio_set_direction(PS2_DAT_GPIO, GPIO_MODE_INPUT);  
    gpio_set_direction(PS2_CMD_GPIO, GPIO_MODE_OUTPUT);  
    gpio_set_direction(PS2_CLK_GPIO, GPIO_MODE_OUTPUT);  
    gpio_set_direction(PS2_CS_GPIO, GPIO_MODE_OUTPUT);  
  
    gpio_set_pull_mode(PS2_DAT_GPIO, GPIO_PULLUP_ONLY);  
}  
  
// 发送一个字节并接收一个字节  
uint8_t ps2_send_receive(uint8_t data) {  
    uint8_t received_data = 0;  
  
    for (int i = 0; i < 8; i++) {  
        // 设置CMD引脚电平  
        gpio_set_level(PS2_CMD_GPIO, (data >> i) & 0x01);  
  
        // 产生时钟下降沿  
        gpio_set_level(PS2_CLK_GPIO, 0);  
        vTaskDelay(1 / portTICK_PERIOD_MS);  
  
        // 读取DAT引脚电平  
        received_data |= (gpio_get_level(PS2_DAT_GPIO) << i);  
  
        // 产生时钟上升沿  
        gpio_set_level(PS2_CLK_GPIO, 1);  
        vTaskDelay(1 / portTICK_PERIOD_MS);  
    }  
  
    return received_data;  
}  
  
// 初始化PS2手柄通信  
void ps2_init() {  
    ps2_gpio_init();  
  
    // 拉低CS引脚开始通信  
    gpio_set_level(PS2_CS_GPIO, 0);  
  
    // 发送初始化命令  
    ps2_send_receive(0x01);  
    ps2_send_receive(0x42);  
  
    // 拉高CS引脚结束通信  
    gpio_set_level(PS2_CS_GPIO, 1);  
}  
  
// 读取PS2手柄数据  
void ps2_read_data() {  
    uint8_t data[9] = {0};  
  
    // 拉低CS引脚开始通信  
    gpio_set_level(PS2_CS_GPIO, 0);  
  
    // 发送数据请求命令  
    ps2_send_receive(0x01);  
  
    // 接收数据  
    for (int i = 0; i < 9; i++) {  
        data[i] = ps2_send_receive(0x00);  
    }  
  
    // 拉高CS引脚结束通信  
    gpio_set_level(PS2_CS_GPIO, 1);  
  
    // 解析数据并打印  
    ESP_LOGI(TAG, "PS2 Data: ");  
    for (int i = 0; i < 9; i++) {  
        ESP_LOGI(TAG, "%02x ", data[i]);  
    }  
    ESP_LOGI(TAG, "\n");  
}  
  
// 主任务  
void app_main() {  
    ps2_init();  
  
    while (1) {  
        ps2_read_data();  
        vTaskDelay(100 / portTICK_PERIOD_MS);  
    }  
}

根据联网搜索工具返回的结果，我收集到了关于PS2手柄协议、ESP32与PS2手柄的硬件连接方案、以及ESP-IDF开发框架的相关信息。以下是对这些信息的总结和分析：

PS2手柄协议规范：
PS2手柄使用SPI类似的通信协议，涉及四条线路：CS（片选）、DAT（MISO）、CMD（MOSI）、CLK（时钟线）。
通信是全双工的，收发同时进行，且每一帧低位先行。
一个通信周期由9帧构成，包括命令和数据的发送与接收。
ESP32与PS2手柄硬件连接方案：
接线涉及数据、命令、地线和电源线。
PS2手柄的接口包括DI、DO、GND、VCC、CS、CLK等引脚。
需要上拉电阻，因为控制器只能连接线对地。
电源电压应在3-5V之间。
ESP-IDF开发框架：
ESP-IDF是乐鑫官方的物联网开发框架，支持ESP32芯片的开发。
提供了GPIO控制、Wi-Fi和蓝牙通信、定时器、SPI和I2C接口等功能。
开发环境搭建涉及安装ESP-IDF、配置环境变量、使用VScode插件等步骤。
现有实现案例：
有基于Arduino框架的ESP32连接PS2手柄的案例，使用了特定的库（如PS2X_lib）来实现。
这些案例提供了接线方案、代码实现以及调试过程的参考。
协议实现细节：
PS2手柄的通信周期包括多个帧，每个帧有特定的含义，如初始化、数据请求、按键状态回复等。
需要处理时钟信号、片选信号以及数据的收发时序。
基于以上信息，我认为实现ESP32模拟PS2手柄协议的关键点在于：

硬件连接：确保ESP32与PS2手柄的接口正确连接，包括数据线、命令线、时钟线和片选线。
协议实现：根据PS2手柄的通信协议，使用ESP-IDF的GPIO功能模拟SPI通信，实现数据的收发和解析。
代码结构：初始化GPIO引脚，配置通信时序，发送命令并接收数据，解析按键状态。
接下来，我将根据这些关键点，调用代码解释器工具编写ESP32模拟PS2手柄协议的代码框架。代码将包括GPIO初始化、通信时序配置、命令发送与数据接收、以及按键状态解析等功能。
